﻿using CivitaiAPI;
using CivitaiCLI.Utils;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using System.Threading.Tasks;

namespace CivitaiCLI
{
    public class CivitaiDownloader
    {
        public static async Task<ModelsResult> GetModels(int page = 1, int perPage = 20)
        {
            ModelsResult result = null;
            string reqUrl = $"https://civitai.com/api/v1/models?sort=Newest&nsfw=true&limit={perPage}&page={page}";
            var request = await InternetUtil.SendGetRequestAsync(reqUrl, "", contentType: "application/json");
            if (request?.ResponseData != null)
            {
                var opts = new System.Text.Json.JsonSerializerOptions() { WriteIndented = false, Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) };
                opts.Converters.Add(new CivitaiWorkarounds.AutoNumberToStringConverter());
                result = System.Text.Json.JsonSerializer.Deserialize<ModelsResult>(request.ResponseData, opts);
            }

            return result;
        }

        public static bool TryParseModelVersion(string query, out int modelVersionID)
        {
            int resultModelVersionID = -1;
            var queryParsed = System.Web.HttpUtility.ParseQueryString(query);
            var modelVersionId = queryParsed.GetValues("modelVersionId");
            if (modelVersionId != null && modelVersionId.Length == 1 && int.TryParse(modelVersionId[0], out int modelID))
            {
                resultModelVersionID = modelID;
            }

            modelVersionID = resultModelVersionID;
            return modelVersionID != -1;
        }

        public static bool TryParseModelUrl(Uri uri, out int id)
        {
            int resultID = -1;
            if (uri.Host.Equals("civitai.com", StringComparison.InvariantCultureIgnoreCase) && uri.Segments.Length >= 3)
            {
                string absUrl = uri.AbsoluteUri;
                string idStr = uri.Segments[2].Trim(new char[] { '/' });
                if (int.TryParse(idStr, NumberStyles.Integer, CultureInfo.InvariantCulture, out int outID))
                {
                    resultID = outID;
                }
            }

            id = resultID;
            return id != -1;
        }

        public static async Task<ModelInfo> GetModelInfoByUrlAsync(Uri uri)
        {
            ModelInfo result = null;
            if (TryParseModelUrl(uri, out int id))
            {
                result = await GetModelInfoByIDAsync(id);
            }

            return result;
        }

        public static async Task<ModelInfo> GetModelInfoByIDAsync(int id)
        {
            ModelInfo result = null;
            var request = await InternetUtil.SendGetRequestAsync($"https://civitai.com/api/v1/models/{id}", "", contentType: "application/json");
            if (request?.ResponseData != null)
            {
                var opts = new System.Text.Json.JsonSerializerOptions() { WriteIndented = false, Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) };
                opts.Converters.Add(new CivitaiWorkarounds.AutoNumberToStringConverter());
                result = System.Text.Json.JsonSerializer.Deserialize<ModelInfo>(request.ResponseData, opts);
            }

            return result;
        }

        public static List<ModelFile> GetModelFiles(ModelVersion modelVersion, ModelPreference pref)
        {
            List<ModelFile> result = new List<ModelFile>();
            if (modelVersion.files == null || modelVersion.files.Count == 0)
                return result;

            //ConsoleUtil.WriteLine(string.Join(", ", modelVersion.files.Select((m) => m.type)), ConsoleColor.DarkYellow);


            //Model
            //VAE
            //Archive
            //Training Data

            if (pref == ModelPreference.None || modelVersion.files.Count == 1)
            {
                result.AddRange(modelVersion.files);
                return result;
            }

            bool isPruned = (ModelPreference.Pruned & pref) == ModelPreference.Pruned;
            bool isFull = (ModelPreference.Full & pref) == ModelPreference.Full;
            bool isF16 = (ModelPreference.F16 & pref) == ModelPreference.F16;
            bool isF32 = (ModelPreference.F32 & pref) == ModelPreference.F32;
            bool preferPruned = isPruned && !isFull;
            bool preferf16 = isF16 && !isF32;
            bool preferPrimary = (ModelPreference.Primary & pref) == ModelPreference.Primary;

            int matchCounter = 0;
            ModelFile bestMatch = null;
            ModelFile fallback = null;

            for (int i = 0; i < modelVersion.files.Count; i++)
            {
                int matches = 0;
                ModelFile file = modelVersion.files[i];
                bool primary = file.primary ?? false;

                if (file.metadata != null)
                {
                    string size = file.metadata?.size ?? "";
                    string fp = file.metadata?.fp ?? "";
                    bool pruned = size.Equals("pruned", StringComparison.InvariantCultureIgnoreCase);
                    bool full = size.Equals("full", StringComparison.InvariantCultureIgnoreCase);
                    bool f16 = fp.Equals("f16", StringComparison.InvariantCultureIgnoreCase);
                    bool f32 = fp.Equals("f32", StringComparison.InvariantCultureIgnoreCase);
                    //file.type                
                }

                if (matches > matchCounter)
                {
                    bestMatch = file;
                    matchCounter = matches;
                }
                else if(fallback == null && primary)
                {
                    fallback = file;
                }
            }

            if (bestMatch != null)
            {
                result.Add(bestMatch);
            }
            else if (fallback != null)
            {
                result.Add(fallback);
            }

            if (result.Count == 0)
            {
                result.AddRange(modelVersion.files);
            }

            return result;
        }
    }
}
